/**
 * WTF Engine
 *
 * Released under the terms of the Worcester Public License,
 * see LICENSE in the root folder. This program comes with
 * no warranty whatsoever, implied or otherwise. All rights
 * other than those explicitly specified in the LICENSE document
 * are reserved.
 **
 * Functors
 **
 * Author: Sean Micklethwaite
 * Magicness: 9/10
 **/

#ifndef __functorH__
#define __functorH__

#include "mm.h"

namespace WtfEngine {
	
	/**** Functors ****/

	template <typename _Ret = void>
	class IFunctor: public virtual IGcObject {
	public:
		virtual _Ret operator ()() = 0;

		// Functor comparison
		virtual bool operator == (const IFunctor& f) const = 0;

		GC_INTERFACE(IFunctor);
	};

#	define FUNCTOR(sName)\
	class sName : public IFunctor<>, public GcObject {\
	public: bool operator == (const IFunctor& f) const {\
		return (typeid(sName) == typeid(&f));\
	};\
	public: void operator()(){

#	define ENDFUNC	}}


	/// Things people don't want to see
	namespace Functors { namespace Detail {
		typedef void Void;

		/// Argument list, terminated by Meta::Meta::Null.
		template <class T, class TS>
		class ArgList: public TS {
		public:
			typedef typename T  tHead;
			typedef typename TS tTail;

			Meta::Null Head;

			inline ArgList(const TS& tail): TS(tail) {};
		};
	
		/// A cons list representing bound arguments. Arguments are bound
		/// right to left.
		template <typename T, typename TS>
		class ArgBinding: public ArgList<T, TS> {
		public:
			typedef ArgBinding<T, TS> tThis;
			typedef T tArg;

			typename Meta::ValueTraits<T>::tStorage Head;

			/// Argument list constructor - prepend the new argument onto existing list
			inline ArgBinding(typename Meta::ValueTraits<T>::tConstRef x, const TS& xs)
				: ArgList<T, TS>(xs), Head(x) {
			};

			/// Compare bound arguments
			inline bool operator == (const tThis& a) const {
				return (Head == a.Head && tTail::operator == (a));
			};
		};

		/// A cons list representing unbound arguments. Can be combined with bound arguments
		/// to give partially bound functors.
		template <typename T, typename TS>
		class ArgSpec: public ArgList<T, TS> {
		public:
			typedef T tArg;

			inline ArgSpec(const TS& tail): ArgList<T, TS>(tail) {};
		};

		/// GetArg - returns the bound argument at _Idx (offset starts from zero)
		template <int _Idx, typename _Binding>
		struct GetArg {
			typedef typename Meta::ListTraits<_Binding> tListTraits;

			typedef typename GetArg<_Idx - 1, typename tListTraits::Tail> _Next;

			typedef typename _Next::tArg       tArg;
			typedef typename _Next::tArgTraits tArgTraits;

			inline static typename tArgTraits::tConstRef get(const _Binding& b) {
				return _Next::get(b);
			};
		};
		template <typename _Binding>
		struct GetArg<0, _Binding> {
			typedef typename Meta::Value<_Binding> tValue;
			typedef typename tValue::tValue	tArg;
			typedef typename Meta::ValueTraits<tArg> tArgTraits;

			inline static typename tArgTraits::tConstRef get(const _Binding& b) {
				return typename tValue::get(b);
			};
		};


		/** Argument macros - can be extended to support more args **/

#	define FUNCTOR_DECL_TPL_ARGS \
		typename _T0 = Meta::Null, typename _T1 = Meta::Null, typename _T2 = Meta::Null,\
		typename _T3 = Meta::Null, typename _T4 = Meta::Null
#	define FUNCTOR_DECL_FTPL_ARGS \
		typename _T0, typename _T1, typename _T2, typename _T3, typename _T4
#	define FUNCTOR_LIST_TPL_ARGS \
		_T0, _T1, _T2, _T3, _T4

	/// Arguments specification list - rightmost argument must be head (as we bind from the right).
#	define FUNCTOR_DECL_ARGSPEC \
	typename Meta::List::Trim<Functors::Detail::ArgSpec<_T4, Functors::Detail::ArgSpec<_T3, Functors::Detail::ArgSpec<_T2, \
		Functors::Detail::ArgSpec<_T1, Functors::Detail::ArgSpec<_T0, Meta::Null> > > > > >::Value

#	define FUNCTOR_DECL_INVOKE_ARG(T, a) \
		typename Meta::ValueTraits<T>::tConstRef a
#	define FUNCTOR_DECL_INVOKE_ARGS \
		FUNCTOR_DECL_INVOKE_ARG(_T0, t0), FUNCTOR_DECL_INVOKE_ARG(_T1, t1), FUNCTOR_DECL_INVOKE_ARG(_T2, t2),\
		FUNCTOR_DECL_INVOKE_ARG(_T3, t3), FUNCTOR_DECL_INVOKE_ARG(_T4, t4)
#	define FUNCTOR_LIST_INVOKE_ARGS \
		t0, t1, t2, t3, t4
#	define FUNCTOR_GET_ARG(i) \
		Functors::Detail::GetArg<i, _Binding>::get(mBinding)
#	define FUNCTOR_ENUM_ARGS \
		FUNCTOR_GET_ARG(0), FUNCTOR_GET_ARG(1), FUNCTOR_GET_ARG(2), FUNCTOR_GET_ARG(3), FUNCTOR_GET_ARG(4)
		
#	define FUNCTOR_PTR(_Ret, n) \
		_Ret (*n)(FUNCTOR_LIST_TPL_ARGS)
#	define FUNCTOR_CLASS_PTR(_Ret, n) \
		_Ret (Meta::ClassTraits<_Class>::tClass::*n)(FUNCTOR_LIST_TPL_ARGS)

		/**
		 * Abstract class representing a curried functor which requires arguments to be bound to
		 * it before it can be called.
		 **/
		template <typename _Ret, class _ArgsSpec>
		class ArgFunctor: public GcObject {
		public:
			typedef ArgFunctor<_Ret, _ArgsSpec> tThis;
			GC_AUTOSIZE(tThis);

		public:
			typedef typename _ArgsSpec::tHead _NextArg;
			typedef typename _ArgsSpec::tTail _NextSpec;

			/// The binding operator - passes an argument to the functor and returns a functor representing
			/// this functor with the bound argument (i.e. the result of applying the arg to the curried function)
			virtual typename ArgFunctor<_Ret, _NextSpec> * Bind(typename Meta::ValueTraits<_NextArg>::tConstRef arg) = 0;
		};

		/// A functor with all arguments bound - can be invoked
		template <typename _Ret>
		class ArgFunctor<_Ret, Meta::Null>: public IFunctor<_Ret>, public GcObject {
		public:
			typedef ArgFunctor<_Ret, Meta::Null> tThis;
			GC_AUTOSIZE(tThis);
		};


		/**
		 * Concrete class representing a curried functor with some number of arguments bound. The
		 * functor can be invoked once all arguments are bound.
		 **/
		template <typename _Ret, class _ArgsSpec, class _Binding, class _Functor>
		class PartialFunctor: public ArgFunctor<_Ret, _ArgsSpec> {
		protected:
			_Binding mBinding; //< Storage for the bound arguments
			_Functor mFunctor; //< Actual wrapper for the function pointer

		public:
			typedef PartialFunctor<_Ret, _ArgsSpec, _Binding, _Functor> tThis;

			typedef Functors::Detail::ArgBinding<_NextArg, _Binding> _NextBinding;

			inline explicit PartialFunctor(const _Binding& args, const _Functor& func)
				: mBinding(args), mFunctor(func) {
			};

			typename ArgFunctor<_Ret, _NextSpec> * Bind(typename Meta::ValueTraits<_NextArg>::tConstRef arg) {
				return new PartialFunctor<_Ret, _NextSpec, _NextBinding, _Functor>(_NextBinding(arg, mBinding), mFunctor);
			};

			inline bool operator == (const tThis& f) const {
				return (mBinding == f.mBinding && mFunctor == f.mFunctor);
			};

			GC_AUTOSIZE(tThis);
		};
		template <typename _Ret, class _Binding, class _Functor>
		class PartialFunctor<_Ret, Meta::Null, _Binding, _Functor>: public ArgFunctor<_Ret, Meta::Null> {
			_Binding mBinding;
			_Functor mFunctor;

		public:
			typedef PartialFunctor<_Ret, Meta::Null, _Binding, _Functor> tThis;

			inline explicit PartialFunctor(const _Binding& args, const _Functor& func)
				: mBinding(args), mFunctor(func) {
			};

			_Ret operator ()() {
				return mFunctor.Invoke(FUNCTOR_ENUM_ARGS);
			};

			inline bool operator == (const tThis& f) const {
				return (mBinding == f.mBinding && mFunctor == f.mFunctor);
			};
			inline bool operator == (const IFunctor& f) const {
				tThis const * pF = dynamic_cast<tThis const *>(&f);
				return (pF != NULL && *this == *pF);
			};

			GC_AUTOSIZE(tThis);
		};
		template <class _Binding, class _Functor>
		class PartialFunctor<Void, Meta::Null, _Binding, _Functor>: public ArgFunctor<Void, Meta::Null> {
			_Binding mBinding;
			_Functor mFunctor;

		public:
			typedef PartialFunctor<Void, Meta::Null, _Binding, _Functor> tThis;

			inline explicit PartialFunctor(const _Binding& args, const _Functor& func)
				: mBinding(args), mFunctor(func) {
			};

			void operator ()() {
				mFunctor.Invoke(FUNCTOR_ENUM_ARGS);
			};

			inline bool operator == (const tThis& f) const {
				return (mBinding == f.mBinding && mFunctor == f.mFunctor);
			};
			inline bool operator == (const IFunctor& f) const {
				tThis const * pF = dynamic_cast<tThis const *>(&f);
				return (pF != NULL && *this == *pF);
			};

			GC_AUTOSIZE(tThis);
		};


		/**** Function Pointer Wrappers ****/
		
		

		/// Wrapper for static/global functions
		template <typename _Ret, FUNCTOR_DECL_TPL_ARGS>
		class StaticFunctor {
		public:
			typedef StaticFunctor<_Ret, FUNCTOR_LIST_TPL_ARGS> tThis;

		private:
			FUNCTOR_PTR(_Ret, mpFunc);

		public:
			template <class _FunctionPtr>
			inline explicit StaticFunctor(_FunctionPtr pFunc)
				: mpFunc(reinterpret_cast<FUNCTOR_PTR(_Ret,)>(pFunc)) {
			};

			/// Invoke the functor with the specified arguments
			/// @warning No type safety. Use at risk of punishment.
			inline _Ret Invoke(FUNCTOR_DECL_INVOKE_ARGS) {
				return (*mpFunc)(FUNCTOR_LIST_INVOKE_ARGS);
			};

			inline bool operator == (const tThis& f) const {
				return (mpFunc == f.mpFunc);
			};
		};
		/// Specialization for void return type
		template <FUNCTOR_DECL_FTPL_ARGS>
		class StaticFunctor<Void, FUNCTOR_LIST_TPL_ARGS> {
		public:
			typedef StaticFunctor<Void, FUNCTOR_LIST_TPL_ARGS> tThis;

		private:
			FUNCTOR_PTR(Functors::Detail::Void, mpFunc);

		public:
			template <class _FunctionPtr>
			inline explicit StaticFunctor(_FunctionPtr pFunc)
				: mpFunc(reinterpret_cast<FUNCTOR_PTR(Functors::Detail::Void,)>(pFunc)) {
			};

			/// Invoke the functor with the specified arguments
			/// @warning No type safety. Use at risk of punishment.
			inline void Invoke(FUNCTOR_DECL_INVOKE_ARGS) {
				(*mpFunc)(FUNCTOR_LIST_INVOKE_ARGS);
			};

			inline bool operator == (const tThis& f) const {
				return (mpFunc == f.mpFunc);
			};
		};

		/**
		 * Function pointer wrapper for non-static class member functions.
		 **/
		template <class _Class, typename _Ret, FUNCTOR_DECL_TPL_ARGS>
		class ObjectFunctor {
		public:
			typedef ObjectFunctor<_Class, _Ret, FUNCTOR_LIST_TPL_ARGS> tThis;
			typedef typename Meta::ClassTraits<_Class>::tPtr tClassPtr;

		private:
			tClassPtr mpObject;
			FUNCTOR_CLASS_PTR(_Ret, mpFunc);

		public:
			// XXX
			// Arr.. we have to mitigate some type safety when passing the function pointer
			template <class _FunctionPtr>
			inline explicit ObjectFunctor(tClassPtr pObject, _FunctionPtr pFunc)
				: mpObject(pObject), mpFunc(reinterpret_cast<FUNCTOR_CLASS_PTR(_Ret,)>(pFunc)) {
			};

			/// Invoke the functor with the specified arguments
			/// @warning No type safety. Use at risk of punishment.
			inline _Ret Invoke(FUNCTOR_DECL_INVOKE_ARGS) {
				return ((*mpObject).*mpFunc)(FUNCTOR_LIST_INVOKE_ARGS);
			};

			inline bool operator == (const tThis& f) const {
				return (mpObject == f.mpObject && mpFunc == f.mpFunc);
			};
		};
		/// Specialization for void return type
		template <class _Class, FUNCTOR_DECL_FTPL_ARGS>
		class ObjectFunctor<_Class, Void, FUNCTOR_LIST_TPL_ARGS> {
		public:
			typedef ObjectFunctor<_Class, Void, FUNCTOR_LIST_TPL_ARGS> tThis;
			typedef typename Meta::ClassTraits<_Class>::tPtr tClassPtr;

		private:
			tClassPtr mpObject;
			FUNCTOR_CLASS_PTR(Functors::Detail::Void, mpFunc);

		public:
			template <class _FunctionPtr>
			inline explicit ObjectFunctor(tClassPtr pObject, _FunctionPtr pFunc)
				: mpObject(pObject), mpFunc(reinterpret_cast<FUNCTOR_CLASS_PTR(Functors::Detail::Void,)>(pFunc)) {
			};

			/// Invoke the functor with the specified arguments
			/// @warning No type safety. Use at risk of punishment.
			inline void Invoke(FUNCTOR_DECL_INVOKE_ARGS) {
				((*mpObject).*mpFunc)(FUNCTOR_LIST_INVOKE_ARGS);
			};

			inline bool operator == (const tThis& f) const {
				return (mpObject == f.mpObject && mpFunc == f.mpFunc);
			};
		};
	}}; // Functors::Detail
};
namespace Meta {
	/// Adaptor for receiving head value from argument binding list
	template <class T, class TS>
	struct Value<WtfEngine::Functors::Detail::ArgBinding<T, TS> > {
		typedef T tValue;
		inline static typename ValueTraits<T>::tConstRef get(const WtfEngine::Functors::Detail::ArgBinding<T, TS>& b) {
			return b.Head;
		};
	};

	template <class T, class TS>
	struct ListTraits<WtfEngine::Functors::Detail::ArgList<T, TS> > {
		typedef typename WtfEngine::Functors::Detail::ArgList<T, TS>::tHead Head;
		typedef typename WtfEngine::Functors::Detail::ArgList<T, TS>::tTail Tail;
	};
};
namespace WtfEngine {

	/**
	 * Utility class for getting the reference type of a functor.
	 **/
	template <typename _Ret = Functors::Detail::Void, FUNCTOR_DECL_TPL_ARGS>
	struct FunctorType {
		typedef typename Functors::Detail::ArgFunctor<_Ret, FUNCTOR_DECL_ARGSPEC >::tRef tRef;
	};


	/**** Concrete Functor Types ****/

	/// Generic base functor
	typedef IFunctor<>	Functor;


	template <typename _Ret = Functors::Detail::Void, FUNCTOR_DECL_TPL_ARGS>
	class StaticFunctor: public Functors::Detail::PartialFunctor<_Ret, FUNCTOR_DECL_ARGSPEC, Meta::Null,
			Functors::Detail::StaticFunctor<_Ret, FUNCTOR_LIST_TPL_ARGS> >
	{
	protected:
		typedef StaticFunctor<_Ret, FUNCTOR_LIST_TPL_ARGS>	tThis;

	public:
		template <class _FunctionPtr>
		explicit StaticFunctor(_FunctionPtr f)
		: Functors::Detail::PartialFunctor<_Ret, FUNCTOR_DECL_ARGSPEC, Meta::Null,
			Functors::Detail::StaticFunctor<_Ret, FUNCTOR_LIST_TPL_ARGS> >(Meta::Null(), 
				Functors::Detail::StaticFunctor<_Ret, FUNCTOR_LIST_TPL_ARGS>(f)) {
		};

		GC_AUTOSIZE(tThis);
	};

	/** DOES NOT reference objects - use with care!! **/
	template <class _Class, typename _Ret = Functors::Detail::Void, FUNCTOR_DECL_TPL_ARGS>
	class RawObjFunctor : public Functors::Detail::PartialFunctor<_Ret, FUNCTOR_DECL_ARGSPEC, Meta::Null,
			Functors::Detail::ObjectFunctor<_Class *, _Ret, FUNCTOR_LIST_TPL_ARGS> >
	{
	protected:
		typedef RawObjFunctor<_Class, _Ret, FUNCTOR_LIST_TPL_ARGS>	tThis;

	public:
		template <class _FunctionPtr>
		RawObjFunctor(_Class* pObj, _FunctionPtr f)
		: Functors::Detail::PartialFunctor<_Ret, FUNCTOR_DECL_ARGSPEC, Meta::Null,
			Functors::Detail::ObjectFunctor<_Class *, _Ret, FUNCTOR_LIST_TPL_ARGS> >(Meta::Null(), 
				Functors::Detail::ObjectFunctor<_Class *, _Ret, FUNCTOR_LIST_TPL_ARGS>(pObj, f)) {
		};

		GC_AUTOSIZE(tThis);
	};


	/**
	 * Wraps a pointer to a non-static member function, with optional return value and arguments.
	 **/
	template <class _Class, typename _Ret = Functors::Detail::Void, FUNCTOR_DECL_TPL_ARGS>
	class ObjFunctor : public Functors::Detail::PartialFunctor<_Ret, FUNCTOR_DECL_ARGSPEC, Meta::Null,
			Functors::Detail::ObjectFunctor<_Class, _Ret, FUNCTOR_LIST_TPL_ARGS> >
	{
	protected:
		typedef ObjFunctor<_Class, _Ret, FUNCTOR_LIST_TPL_ARGS>	tThis;

	public:
		template <class _FunctionPtr>
		ObjFunctor(typename Meta::ClassTraits<_Class>::tPtr pObj, _FunctionPtr f)
		: Functors::Detail::PartialFunctor<_Ret, FUNCTOR_DECL_ARGSPEC, Meta::Null,
			Functors::Detail::ObjectFunctor<_Class, _Ret, FUNCTOR_LIST_TPL_ARGS> >(Meta::Null(), 
				Functors::Detail::ObjectFunctor<_Class, _Ret, FUNCTOR_LIST_TPL_ARGS>(pObj, f)) {
		};

		GC_AUTOSIZE(tThis);
	};

	/*template <class T, class tArg, typename tFuncArg = tArg>
	class BoundObjFunctor : public ObjFunctor<T>
	{
	public:
		typedef BoundObjFunctor<T, tArg, tFuncArg>	tThis;
		typedef void (T::*tFunc)(tFuncArg);

	private:
		tArg	mArg;

	public:
		BoundObjFunctor(const GcReference<T>& rObj, const tFunc f)
			: ObjFunctor<T>(rObj, (typename ObjFunctor<T>::tFunc)f) {};
		BoundObjFunctor(const GcReference<T>& rObj, const tFunc f, const tArg& a)
			: ObjFunctor<T>(rObj, (typename ObjFunctor<T>::tFunc)f), mArg(a) {};

		void Bind(const tArg& a){
			mArg = a;
		};

		void operator ()(){
			((&*this->mrObject)->*(tFunc)(this->mpFunction))(mArg);
		};

		GC_AUTOSIZE(tThis);
	};*/
};

#endif
